<script setup lang="ts">
import F_chat_record from "@/components/common/f_chat_record.vue";
import {useRoute} from "vue-router";
import {IconImage} from "@arco-design/web-vue/es/icon";
import {fetchEventSource} from '@microsoft/fetch-event-source';
import {nextTick, onMounted, reactive, ref, watch} from "vue";
import {userStorei} from "@/stores/user_store";
import type {baseResponse} from "@/api";
import {Message} from "@arco-design/web-vue";
import type {sessionDetailType} from "@/api/session_list_api";
import {sessionDetailApi} from "@/api/session_list_api";
import {sessionCreateApi} from "@/api/session_list_api";
import router from "@/router";
import type {chatCreateType} from "@/api/chat_api";
import {imageUploadApi} from "@/api/image_api";
import {base64UrlToFile} from "@/utils/file";

const store = userStorei()
const route = useRoute()

const body = reactive<chatCreateType>({
  sessionID: Number(route.params.id),
  content: "",
  imageList: []
})

const iptRef = ref()
const sessionDetail = reactive<sessionDetailType>({
  "id": 17,
  "createdAt": "2024-09-22T23:28:30.507+08:00",
  "userID": 10,
  "roleID": 12,
  "title": "与go开发助手2的会话",
  "roleTitle": "go开发助手2",
  "roleAvatar": "",
  "roleAbstract": "这是简介",
  "roleUserChatCount": 7,
})

const fChatRecordRef = ref()
const disable = ref(false)

function chat(e: KeyboardEvent) {
  if (body.content.trim() === "") {
    return
  }

  // 判断这里有没有ctrlKey
  if (e.ctrlKey || e.shiftKey) {
    body.content += "\n"
    return
  }
  chatHandler()
}
function chatHandler(){
  const controller = new AbortController()
  const signal = controller.signal
  const chat = reactive({
    "id": 0,
    "createdAt": new Date(),
    "sessionID": Number(route.params.id),
    "roleID": 0,
    "userID": store.userInfo.userID,
    "userContent": body.content,
    "aiContent": "",
    "sessionTitle": sessionDetail.title,
    "roleTitle": sessionDetail.roleTitle,
    "roleAvatar": sessionDetail.roleAvatar,
    "userNickname": store.userInfo.nickName,
    "userAvatar": store.userInfo.avatar,
    "imageList": body.imageList
  })
   

  fetchEventSource('/api/chat/sse', {
    method: 'POST',
    signal: signal,
    headers: {
      "token": store.userInfo.token,
    },

    body: JSON.stringify(body),
    async onopen(response) {//建立连接的回调
      body.content = ""
      body.imageList = []
      store.chat.imageList = []
      store.chat.content = ""
    },
    onmessage(msg) {//接收一次数据段时回调，因为是流式返回，所以这个回调会被调用多次
      if (msg.event == '') {
        // 进行连接正常的操作
        const res = JSON.parse(msg.data) as baseResponse<string>
        if (res.code) {
          Message.error(res.msg)
          controller.abort();
          return
        }
        chat.aiContent += res.data
      } else if (msg.event === 'close') {
        controller.abort();
      }
    },
    onclose() {//正常结束的回调
      scrollbarBottom()
      disable.value = false
      focusInput(); // 自动聚焦输入框
    },
    onerror(err) {//连接出现异常回调
      // 必须抛出错误才会停止
      focusInput(); // 自动聚焦输入框
      disable.value = false
      throw err
    }
  })
}



onMounted(()=>{
  if (store.chat.content != ""){
    body.content = store.chat.content
    body.imageList = store.chat.imageList
    setTimeout(()=>{
      chatHandler()
    }, 1000)
  }
})


function scrollbarBottom() {
  const box = document.querySelector(".arco-checkbox-group")
  if (!box) {
    return
  }
  const h = box.scrollHeight
  const div = document.querySelector(".f_chat_record")
  if (!div) {
    return;
  }
  div.scrollTo({top: h, behavior: "smooth"})
}

async function getSessionDetail() {
 nextTick(()=>{
   iptRef.value?.focus()
 })
  const id = Number(route.params.id)
  if (isNaN(id)) {
    return
  }
  const res = await sessionDetailApi(id)
  if (res.code) {
    Message.error(res.msg)
    return
  }
  Object.assign(sessionDetail, res.data)
}
watch(() => route.params.id, () => {
  body.sessionID = Number(route.params.id)
  getSessionDetail()
}, {immediate: true})

async function newSession() {
  const res = await sessionCreateApi(sessionDetail.roleID)
  if (res.code) {
    Message.error(res.msg)
    return
  }
  router.push({name: "chat", params: {id: res.data}})
  Message.success("开启新会话成功")
}

const imgIptRef = ref()

function imgClick() {
  imgIptRef.value?.click()
}

async function imageChange(e: Event) {
  const target = e.target as HTMLInputElement
  if (target.files && target.files.length) {
    const file = target.files[0]
    const res = await imageUploadApi(file)
    if (res.code) {
      Message.error(res.msg)
      return
    }
    Message.success(res.msg)
    body.imageList?.push(res.data)
  }
}

function paste(event: ClipboardEvent) {
  // 取消默认事件
  // event.preventDefault();
  const items = (event.clipboardData as DataTransfer).items;
  for (const index in items) {
    const item = items[index];
    if (item.kind === 'file') {
      const blob = item.getAsFile();
      const reader = new FileReader();
      reader.onload = async (event: ProgressEvent<FileReader>) => {
        // 这里是在前端显示图片，可以自由操作是上传后端还是进行文件操作
        const file = base64UrlToFile((event.target as FileReader).result as string, "img.png")
        // console.log(file)
        const res = await imageUploadApi(file)
        if (res.code) {
          Message.error(res.msg)
          return
        }
        Message.success(res.msg)
        body.imageList?.push(res.data)
        // console.log(event)
        return
      };
      reader.readAsDataURL(blob as File);
    }
  }
}
function focusInput() {
  nextTick(() => {
      iptRef.value.focus(); // 确保调用 focus() 方法
  });
}



</script>

<template>
  <div class="chat_view">
    <f_chat_record ref="fChatRecordRef" :session-id="Number(route.params.id) as number"></f_chat_record>
    <div class="chat_ipt">
      <div class="line up_images_show" v-if="body.imageList?.length">
        <a-image :height="60" :src="src" v-for="src in body.imageList"></a-image>
      </div>
      <div class="line chat_ipt_inner">
        <a-textarea @paste="paste" :auto-size="{minRows: 4, maxRows: 6}" @keydown.enter="chat" :disabled="disable"
                    ref="iptRef"
                    placeholder="输入你的问题"
                    v-model="body.content"></a-textarea>

        <div class="actions">
          <div class="tip">按Enter键发送，Ctrl+Enter换行</div>
          <a-button @click="newSession" type="outline" title="开启新的会话">
            <i class="iconfont icon-magic-full"></i>
          </a-button>
          <a-button @click="imgClick" class="up_img" type="outline" title="上传图片">
            <IconImage></IconImage>
          </a-button>
          <input style="display: none" ref="imgIptRef" @change="imageChange" type="file" accept="image/*">
          <a-button type="primary" :disabled="body.content.trim() === ''" @click="chatHandler" >搜索</a-button>
        </div>
      </div>
      <div class="line ai_tips">
        内容由 AI 大模型生成，请仔细甄别
      </div>
    </div>
  </div>
</template>

<style lang="less">
.chat_view {

  .f_chat_record {
    padding: 0;
    height: calc(100vh - 200px);
    min-height: inherit;
    padding-right: 10px;

    .item_p {
      padding: 0;
      padding-top: 20px;
    }

    .date {
      top: 7px;
      right: 0;
    }
  }

  .chat_ipt {
    margin: 10px 0;
    position: fixed;
    bottom: 0;
    width: 1200px;

    .arco-textarea {
      &::-webkit-scrollbar {
        width: 10px; /* 滚动条宽度 */
        height: 10px;
      }

      &::-webkit-scrollbar-thumb {
        background: var(--color-fill-4); /* 滚动条颜色 */
        border-radius: 5px; /* 滚动条圆角 */
        position: fixed;
        z-index: 1;
        cursor: pointer;
      }

      &::-webkit-scrollbar-track {
        background: transparent; /* 滚动条背景色 */
      }

    }

    .chat_ipt_inner {
      position: relative;
    }

    .line {
      display: flex;
      justify-content: center;

      &.up_images_show {
        justify-content: start;
        background-color: var(--color-fill-2);
        width: fit-content;
        padding: 5px;
        border-radius: 5px;
        margin-bottom: 5px;

        > div {
          margin-right: 10px;

          &:last-child {
            margin-right: 0;
          }
        }
      }
    }

    .ai_tips {
      font-size: 12px;
      color: var(--color-text-2);
      margin-top: 5px;
    }

    .actions {
      position: absolute;
      right: 10px;
      bottom: 10px;
      display: flex;
      align-items: end;
      z-index: 1;

      .tip {
        font-size: 12px;
        color: var(--color-text-2);
        margin-right: 10px;
      }

      .up_img {
        margin: 0 10px;
      }
    }
  }
}
</style>